fn main() {
    test1();
    test2();
}

fn test1() {
    use std::mem;

    let color = String::from("green");

    // A closure to print `color` which immediately borrow (`&`) `color` and
    // stores the borrow and closure in the `print` variable, it will remain
    // borrowed until `print` is used the last time
    // 
    // `println` only requires arguments by immutable reference so it doesn't
    // impose anything more restrictive.
    let print = || println!("color: {}", color);

    // call the closure using the borrow
    print();

    // `color` can be borrowed immutably again, because the closure only holds
    // an immutable reference to `color`
    let _reborrow = &color;
    print();

    // a move or reborrow is allowed after the final use of `print`
    let _color_moved = color;

    // if we uncomment the following line, the compiler will report:cannot move out of `color` because it is borrowed as i expected
    // print();

    let mut count = 0;

    // a closure to increment `count` could take either `&mut count` or `count`
    // but `&mut count` is less restrictive so it takes that. immediately
    // borrows `count`
    // 
    // a `mut` is required on `inc` because a `&mut` is stored inside. thus,
    // calling the closure mutates `count` which requires a `mut`
    let mut inc = || {
        count += 1;
        println!("count: {}", count);
    };

    // call the closure using a mutable borrow
    inc();

    inc();

    // the closure still mutably borrows `count` because it is called later.
    // an attempt to reborrow will lead to an error.
    // let _reborrow = &mut count;
    // ^ TODO: try uncommenting this line.

    inc();

    // the closure no longer needs to borrow `&mut count`. therefore, it is
    // possible to reborrow without an error
    let _count_reborrowed = &mut count;

    // a non-copy type.
    let movable = Box::new(3);

    // `mem::drop` requires `T` so this must take by value. a copy type
    // would copy into the closure leaving the original untouched.
    // a non copy must move and so `movable` immediately moves into
    // the closure.
    let consume = || {
        println!("`movable`: {:?}", movable);
        mem::drop(movable);
    };

    // `consume` consumes the variable so this can only be called once.
    consume();
    // consume();
    // ^ TODO: try uncommenting this line.
    // if we uncomment that line, compiler will report that:use of moved value: `consume`
}

fn test2() {
    // `Vec` has non-copy semantics.
    let haystack = vec![1,2,3];
    let contains = move |needle| {haystack.contains(needle)};

    println!("{}", contains(&1));
    println!("{}", contains(&4));

    // println!("There're {} elements in vec", haystack.len());
    // ^ Uncommenting above line will result in compile-time error
    // because borrow checker doesn't allow re-using variable after it
    // has been moved.
    
    // Removing `move` from closure's signature will cause closure
    // to borrow _haystack_ variable immutably, hence _haystack_ is still
    // available and uncommenting above line will not cause an error.
}